﻿namespace CSMAAlgorithm.test
{
    using System;
    using System.Collections.Generic;

    using Microsoft.VisualStudio.TestTools.UnitTesting;

    using Moq;

    using Shared;

    [TestClass]
    public class CSMAPersistenAlgorithmFixture
    {
        [TestMethod]
        public void ShouldFailWithZeroClients()
        {
            const int NumberOfClients = 0;
            const double AverageNumberOfFramesPerClient = 0;

            var frame = new Frame(1);

            var successDistribution = new Mock<IDistribution>();

            IAlgorithm algorithm = this.CreateAlgorithm(successDistribution.Object);
            algorithm.Send(frame, NumberOfClients, AverageNumberOfFramesPerClient, It.IsAny<Action<int, double>>());

            Assert.IsFalse(algorithm.WasTransmissionSuccessful(frame));
        }

        [TestMethod]
        public void ShouldNotFailWithOneClient()
        {
            const int NumberOfClients = 1;
            const int AverageNumberOfFramesPerClient = 1;

            var frame = new Frame(1);

            var successDistribution = new Mock<IDistribution>();
            successDistribution.Setup(x => x.NextDouble(1, NumberOfClients + 1)).Returns(1);

            IAlgorithm algorithm = this.CreateAlgorithm(successDistribution.Object);
            algorithm.Send(frame, NumberOfClients, AverageNumberOfFramesPerClient, It.IsAny<Action<int, double>>());

            Assert.IsTrue(algorithm.WasTransmissionSuccessful(frame));
        }

        [TestMethod]
        public void ShouldFailIfFrameWasNotSent()
        {
            const int NumberOfClients = 2;
            const int AverageNumberOfFramesPerClient = 1;

            var frame = new Frame(1);

            var successDistribution = new Mock<IDistribution>();
            successDistribution.Setup(x => x.NextDouble(1, NumberOfClients + 1)).Returns(0.5);

            IAlgorithm algorithm = this.CreateAlgorithm(successDistribution.Object);
            algorithm.Send(frame, NumberOfClients, AverageNumberOfFramesPerClient, It.IsAny<Action<int, double>>());

            Assert.IsFalse(algorithm.WasTransmissionSuccessful(frame));
        }

        [TestMethod]
        public void ShouldReGetDistributionValueIfTransmisionFail()
        {
            const int NumberOfClients = 2;
            const int AverageNumberOfFramesPerClient = 1;

            var frame = new Frame(30);

            var successDistribution = new Mock<IDistribution>();
            successDistribution.Setup(x => x.NextDouble(1, NumberOfClients + 1)).Returns(0.5);

            IAlgorithm algorithm = this.CreateAlgorithm(successDistribution.Object);
            algorithm.Send(frame, NumberOfClients, AverageNumberOfFramesPerClient, It.IsAny<Action<int, double>>());

            successDistribution.Verify(x => x.NextDouble(1, NumberOfClients + 1), Times.Exactly(algorithm.AllowedRetries));
        }

        [TestMethod]
        public void ShouldReturnTheTimeElapsedToSend()
        {
            const int NumberOfClients = 1;
            const int DeltaTime = 100;

            var successDistribution = new Mock<IDistribution>();
            successDistribution.Setup(x => x.NextDouble(1, NumberOfClients + 1))
                .Returns(1);

            IAlgorithm algorithm = this.CreateAlgorithm(successDistribution.Object);
            var elapsedTime = algorithm.Send(new Frame(1), NumberOfClients, DeltaTime, It.IsAny<Action<int, double>>());

            Assert.AreEqual(elapsedTime, DeltaTime);
        }

        [TestMethod]
        public void ShouldReturnTheIntegerMaxValueEvenIfTheTransmissionFails()
        {
            const int NumberOfClients = 1;
            const int DeltaTime = 100;

            var successDistribution = new Mock<IDistribution>();
            successDistribution.Setup(x => x.NextDouble(1, NumberOfClients + 1))
                .Returns(0.5);

            IAlgorithm algorithm = this.CreateAlgorithm(successDistribution.Object);
            var elapsedTime = algorithm.Send(new Frame(30), NumberOfClients, DeltaTime, It.IsAny<Action<int, double>>());

            Assert.AreEqual(elapsedTime, int.MaxValue);
        }

        protected virtual IAlgorithm CreateAlgorithm(IDistribution distribution)
        {
            return new CSMAAlgorithm.CSMAPersistent(distribution);
        }
    }
}
